Skip to content

Conversation

philnik777
Copy link
Contributor

@philnik777 philnik777 commented Oct 9, 2025

This simplifies the library quite a bit, since we don't need to differentiate anymore between the classic and the ranges versions of these aliases. This results in us being more forgiving in the classic algorithms, but since they're already not really enforcing the semantic requirements I don't think that's a big problem.

Copy link

github-actions bot commented Oct 9, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions ,cpp,h -- libcxx/include/__algorithm/copy.h libcxx/include/__algorithm/copy_backward.h libcxx/include/__algorithm/count.h libcxx/include/__algorithm/count_if.h libcxx/include/__algorithm/is_permutation.h libcxx/include/__algorithm/iterator_operations.h libcxx/include/__algorithm/lexicographical_compare_three_way.h libcxx/include/__algorithm/make_heap.h libcxx/include/__algorithm/mismatch.h libcxx/include/__algorithm/move.h libcxx/include/__algorithm/move_backward.h libcxx/include/__algorithm/pstl.h libcxx/include/__algorithm/radix_sort.h libcxx/include/__algorithm/ranges_count.h libcxx/include/__algorithm/ranges_count_if.h libcxx/include/__algorithm/ranges_unique_copy.h libcxx/include/__algorithm/sample.h libcxx/include/__algorithm/sift_down.h libcxx/include/__algorithm/stable_sort.h libcxx/include/__algorithm/unique_copy.h libcxx/include/__debug_utils/strict_weak_ordering_check.h libcxx/include/__flat_set/flat_multiset.h libcxx/include/__flat_set/flat_set.h libcxx/include/__iterator/bounded_iter.h libcxx/include/__iterator/cpp17_iterator_concepts.h libcxx/include/__iterator/iterator_traits.h libcxx/include/__iterator/static_bounded_iter.h libcxx/include/__iterator/wrap_iter.h libcxx/include/__numeric/pstl.h libcxx/include/__pstl/backends/default.h libcxx/include/__pstl/backends/libdispatch.h libcxx/include/__pstl/cpu_algos/find_if.h libcxx/include/__pstl/cpu_algos/transform.h libcxx/include/__pstl/cpu_algos/transform_reduce.h libcxx/include/__vector/vector.h libcxx/include/deque libcxx/include/forward_list libcxx/include/list libcxx/include/queue libcxx/include/set libcxx/include/stack libcxx/include/string libcxx/include/unordered_set libcxx/test/libcxx/algorithms/cpp17_iterator_concepts.verify.cpp --diff_from_common_commit

⚠️
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing origin/main to the base branch/commit you want to compare against.
⚠️

View the diff from clang-format here.
diff --git a/libcxx/include/__algorithm/unique_copy.h b/libcxx/include/__algorithm/unique_copy.h
index d591b8b3c..10be73021 100644
--- a/libcxx/include/__algorithm/unique_copy.h
+++ b/libcxx/include/__algorithm/unique_copy.h
@@ -109,9 +109,7 @@ unique_copy(_InputIterator __first, _InputIterator __last, _OutputIterator __res
                        typename iterator_traits<_OutputIterator>::value_type>::value,
           __unique_copy_tags::__reread_from_output_tag,
           __unique_copy_tags::__read_from_tmp_value_tag> >;
-  return std::__unique_copy(
-             std::move(__first), std::move(__last), std::move(__result), __pred, __algo_tag())
-      .second;
+  return std::__unique_copy(std::move(__first), std::move(__last), std::move(__result), __pred, __algo_tag()).second;
 }
 
 template <class _InputIterator, class _OutputIterator>

@philnik777 philnik777 force-pushed the merge_iter_traits_aliases branch 2 times, most recently from 6d5fe98 to 44b6383 Compare October 10, 2025 07:27
@philnik777 philnik777 marked this pull request as ready for review October 10, 2025 07:29
@philnik777 philnik777 requested a review from a team as a code owner October 10, 2025 07:29
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 10, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 10, 2025

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

This simplifies the library quite a bit, since we don't need to differentiate anymore between the classic and the ranges versions of these aliases. This results in us being more forgiving in the classic algorithms, but since they're already not really enforcing the semantic requirements I don't think that's a big problem.


Patch is 83.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/162661.diff

44 Files Affected:

  • (modified) libcxx/include/__algorithm/copy.h (+1-2)
  • (modified) libcxx/include/__algorithm/copy_backward.h (+1-2)
  • (modified) libcxx/include/__algorithm/count.h (+7-8)
  • (modified) libcxx/include/__algorithm/count_if.h (+4-5)
  • (modified) libcxx/include/__algorithm/is_permutation.h (+4-4)
  • (modified) libcxx/include/__algorithm/iterator_operations.h (+10-25)
  • (modified) libcxx/include/__algorithm/lexicographical_compare_three_way.h (+4-4)
  • (modified) libcxx/include/__algorithm/make_heap.h (+2-2)
  • (modified) libcxx/include/__algorithm/mismatch.h (+1-1)
  • (modified) libcxx/include/__algorithm/move.h (+1-2)
  • (modified) libcxx/include/__algorithm/move_backward.h (+1-2)
  • (modified) libcxx/include/__algorithm/pstl.h (+2-2)
  • (modified) libcxx/include/__algorithm/radix_sort.h (+21-22)
  • (modified) libcxx/include/__algorithm/ranges_count.h (+2-4)
  • (modified) libcxx/include/__algorithm/ranges_count_if.h (+2-2)
  • (modified) libcxx/include/__algorithm/ranges_unique_copy.h (+2-2)
  • (modified) libcxx/include/__algorithm/sample.h (+1-1)
  • (modified) libcxx/include/__algorithm/sift_down.h (+2-2)
  • (modified) libcxx/include/__algorithm/stable_sort.h (+1-1)
  • (modified) libcxx/include/__algorithm/unique_copy.h (+5-5)
  • (modified) libcxx/include/__debug_utils/strict_weak_ordering_check.h (+1-1)
  • (modified) libcxx/include/__flat_set/flat_multiset.h (+4-4)
  • (modified) libcxx/include/__flat_set/flat_set.h (+4-5)
  • (modified) libcxx/include/__iterator/bounded_iter.h (+2-2)
  • (modified) libcxx/include/__iterator/cpp17_iterator_concepts.h (+12-13)
  • (modified) libcxx/include/__iterator/iterator_traits.h (+43-33)
  • (modified) libcxx/include/__iterator/static_bounded_iter.h (+2-2)
  • (modified) libcxx/include/__iterator/wrap_iter.h (+6-6)
  • (modified) libcxx/include/__numeric/pstl.h (+2-2)
  • (modified) libcxx/include/__pstl/backends/default.h (+14-14)
  • (modified) libcxx/include/__pstl/backends/libdispatch.h (+2-2)
  • (modified) libcxx/include/__pstl/cpu_algos/find_if.h (+1-1)
  • (modified) libcxx/include/__pstl/cpu_algos/transform.h (+6-5)
  • (modified) libcxx/include/__pstl/cpu_algos/transform_reduce.h (+4-5)
  • (modified) libcxx/include/__vector/vector.h (+3-3)
  • (modified) libcxx/include/deque (+3-3)
  • (modified) libcxx/include/forward_list (+3-3)
  • (modified) libcxx/include/list (+3-3)
  • (modified) libcxx/include/queue (+9-11)
  • (modified) libcxx/include/set (+8-8)
  • (modified) libcxx/include/stack (+2-2)
  • (modified) libcxx/include/string (+1-1)
  • (modified) libcxx/include/unordered_set (+16-23)
  • (modified) libcxx/test/libcxx/algorithms/cpp17_iterator_concepts.verify.cpp (+2-2)
diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index 21fd25ce6fcdc..a4344c0d711cf 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -197,8 +197,7 @@ struct __copy_impl {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
   operator()(_InIter __first, _InIter __last, _OutIter __result) const {
     using _Traits = __segmented_iterator_traits<_OutIter>;
-    using _DiffT =
-        typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
+    using _DiffT  = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;
 
     if (__first == __last)
       return std::make_pair(std::move(__first), std::move(__result));
diff --git a/libcxx/include/__algorithm/copy_backward.h b/libcxx/include/__algorithm/copy_backward.h
index 6c9eba672e154..48a365a1079e8 100644
--- a/libcxx/include/__algorithm/copy_backward.h
+++ b/libcxx/include/__algorithm/copy_backward.h
@@ -214,8 +214,7 @@ struct __copy_backward_impl {
 
     auto __local_last = _Traits::__local(__result);
     while (true) {
-      using _DiffT =
-          typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
+      using _DiffT = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;
 
       auto __local_first = _Traits::__begin(__segment_iterator);
       auto __size        = std::min<_DiffT>(__local_last - __local_first, __last - __first);
diff --git a/libcxx/include/__algorithm/count.h b/libcxx/include/__algorithm/count.h
index 8529d110a30aa..eef77c0055340 100644
--- a/libcxx/include/__algorithm/count.h
+++ b/libcxx/include/__algorithm/count.h
@@ -10,7 +10,6 @@
 #ifndef _LIBCPP___ALGORITHM_COUNT_H
 #define _LIBCPP___ALGORITHM_COUNT_H
 
-#include <__algorithm/iterator_operations.h>
 #include <__algorithm/min.h>
 #include <__bit/invert_if.h>
 #include <__bit/popcount.h>
@@ -31,10 +30,10 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 // generic implementation
-template <class _AlgPolicy, class _Iter, class _Sent, class _Tp, class _Proj>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename _IterOps<_AlgPolicy>::template __difference_type<_Iter>
+template <class _Iter, class _Sent, class _Tp, class _Proj>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iter_difference_t<_Iter>
 __count(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
-  typename _IterOps<_AlgPolicy>::template __difference_type<_Iter> __r(0);
+  __iter_difference_t<_Iter> __r(0);
   for (; __first != __last; ++__first)
     if (std::__invoke(__proj, *__first) == __value)
       ++__r;
@@ -71,8 +70,8 @@ __count_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_t
   return __r;
 }
 
-template <class, class _Cp, bool _IsConst, class _Tp, class _Proj, __enable_if_t<__is_identity<_Proj>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iterator_difference_type<__bit_iterator<_Cp, _IsConst> >
+template <class _Cp, bool _IsConst, class _Tp, class _Proj, __enable_if_t<__is_identity<_Proj>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iter_difference_t<__bit_iterator<_Cp, _IsConst> >
 __count(__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, const _Tp& __value, _Proj&) {
   if (__value)
     return std::__count_bool<true>(
@@ -82,10 +81,10 @@ __count(__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __l
 }
 
 template <class _InputIterator, class _Tp>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iterator_difference_type<_InputIterator>
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __iter_difference_t<_InputIterator>
 count(_InputIterator __first, _InputIterator __last, const _Tp& __value) {
   __identity __proj;
-  return std::__count<_ClassicAlgPolicy>(__first, __last, __value, __proj);
+  return std::__count(__first, __last, __value, __proj);
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/count_if.h b/libcxx/include/__algorithm/count_if.h
index 26f945e6bd98c..46fa2e64c9b74 100644
--- a/libcxx/include/__algorithm/count_if.h
+++ b/libcxx/include/__algorithm/count_if.h
@@ -10,7 +10,6 @@
 #ifndef _LIBCPP___ALGORITHM_COUNT_IF_H
 #define _LIBCPP___ALGORITHM_COUNT_IF_H
 
-#include <__algorithm/iterator_operations.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
@@ -22,10 +21,10 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _AlgPolicy, class _Iter, class _Sent, class _Proj, class _Pred>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __policy_iter_diff_t<_AlgPolicy, _Iter>
+template <class _Iter, class _Sent, class _Proj, class _Pred>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __iter_difference_t<_Iter>
 __count_if(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
-  __policy_iter_diff_t<_AlgPolicy, _Iter> __counter(0);
+  __iter_difference_t<_Iter> __counter(0);
   for (; __first != __last; ++__first) {
     if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
       ++__counter;
@@ -38,7 +37,7 @@ template <class _InputIterator, class _Predicate>
 typename iterator_traits<_InputIterator>::difference_type
 count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
   __identity __proj;
-  return std::__count_if<_ClassicAlgPolicy>(__first, __last, __pred, __proj);
+  return std::__count_if(__first, __last, __pred, __proj);
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/is_permutation.h b/libcxx/include/__algorithm/is_permutation.h
index 86f469c2799c5..0908001cd1a24 100644
--- a/libcxx/include/__algorithm/is_permutation.h
+++ b/libcxx/include/__algorithm/is_permutation.h
@@ -78,7 +78,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation_impl(
     _Pred&& __pred,
     _Proj1&& __proj1,
     _Proj2&& __proj2) {
-  using _D1 = __iterator_difference_type<_Iter1>;
+  using _D1 = __iter_difference_t<_Iter1>;
 
   for (auto __i = __first1; __i != __last1; ++__i) {
     //  Have we already counted the number of *__i in [f1, l1)?
@@ -126,7 +126,7 @@ template <class _AlgPolicy, class _ForwardIterator1, class _Sentinel1, class _Fo
     return true;
 
   //  __first1 != __last1 && *__first1 != *__first2
-  using _D1 = __iterator_difference_type<_ForwardIterator1>;
+  using _D1 = __iter_difference_t<_ForwardIterator1>;
   _D1 __l1  = _IterOps<_AlgPolicy>::distance(__first1, __last1);
   if (__l1 == _D1(1))
     return false;
@@ -173,10 +173,10 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
   if (__first2 == __last2) // Second range is shorter
     return false;
 
-  using _D1 = __iterator_difference_type<_Iter1>;
+  using _D1 = __iter_difference_t<_Iter1>;
   _D1 __l1  = _IterOps<_AlgPolicy>::distance(__first1, __last1);
 
-  using _D2 = __iterator_difference_type<_Iter2>;
+  using _D2 = __iter_difference_t<_Iter2>;
   _D2 __l2  = _IterOps<_AlgPolicy>::distance(__first2, __last2);
   if (__l1 != __l2)
     return false;
diff --git a/libcxx/include/__algorithm/iterator_operations.h b/libcxx/include/__algorithm/iterator_operations.h
index e5c89c1e67e3a..375ac1db1a1a8 100644
--- a/libcxx/include/__algorithm/iterator_operations.h
+++ b/libcxx/include/__algorithm/iterator_operations.h
@@ -47,15 +47,9 @@ struct _RangeAlgPolicy {};
 
 template <>
 struct _IterOps<_RangeAlgPolicy> {
-  template <class _Iter>
-  using __value_type _LIBCPP_NODEBUG = iter_value_t<_Iter>;
-
   template <class _Iter>
   using __iterator_category _LIBCPP_NODEBUG = ranges::__iterator_concept<_Iter>;
 
-  template <class _Iter>
-  using __difference_type _LIBCPP_NODEBUG = iter_difference_t<_Iter>;
-
   static constexpr auto advance      = ranges::advance;
   static constexpr auto distance     = ranges::distance;
   static constexpr auto __iter_move  = ranges::iter_move;
@@ -71,15 +65,9 @@ struct _ClassicAlgPolicy {};
 
 template <>
 struct _IterOps<_ClassicAlgPolicy> {
-  template <class _Iter>
-  using __value_type _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::value_type;
-
   template <class _Iter>
   using __iterator_category _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::iterator_category;
 
-  template <class _Iter>
-  using __difference_type _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::difference_type;
-
   // advance
   template <class _Iter, class _Distance>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static void advance(_Iter& __iter, _Distance __count) {
@@ -164,17 +152,17 @@ struct _IterOps<_ClassicAlgPolicy> {
 
   // advance with sentinel, a la std::ranges::advance
   template <class _Iter>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_Iter>
-  __advance_to(_Iter& __iter, __difference_type<_Iter> __count, const _Iter& __sentinel) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_Iter>
+  __advance_to(_Iter& __iter, __iter_difference_t<_Iter> __count, const _Iter& __sentinel) {
     return _IterOps::__advance_to(__iter, __count, __sentinel, typename iterator_traits<_Iter>::iterator_category());
   }
 
 private:
   // advance with sentinel, a la std::ranges::advance -- InputIterator specialization
   template <class _InputIter>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_InputIter> __advance_to(
-      _InputIter& __iter, __difference_type<_InputIter> __count, const _InputIter& __sentinel, input_iterator_tag) {
-    __difference_type<_InputIter> __dist = 0;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_InputIter> __advance_to(
+      _InputIter& __iter, __iter_difference_t<_InputIter> __count, const _InputIter& __sentinel, input_iterator_tag) {
+    __iter_difference_t<_InputIter> __dist = 0;
     for (; __dist < __count && __iter != __sentinel; ++__dist)
       ++__iter;
     return __count - __dist;
@@ -182,12 +170,12 @@ struct _IterOps<_ClassicAlgPolicy> {
 
   // advance with sentinel, a la std::ranges::advance -- BidirectionalIterator specialization
   template <class _BiDirIter>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_BiDirIter>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_BiDirIter>
   __advance_to(_BiDirIter& __iter,
-               __difference_type<_BiDirIter> __count,
+               __iter_difference_t<_BiDirIter> __count,
                const _BiDirIter& __sentinel,
                bidirectional_iterator_tag) {
-    __difference_type<_BiDirIter> __dist = 0;
+    __iter_difference_t<_BiDirIter> __dist = 0;
     if (__count >= 0)
       for (; __dist < __count && __iter != __sentinel; ++__dist)
         ++__iter;
@@ -199,9 +187,9 @@ struct _IterOps<_ClassicAlgPolicy> {
 
   // advance with sentinel, a la std::ranges::advance -- RandomIterator specialization
   template <class _RandIter>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __difference_type<_RandIter>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 static __iter_difference_t<_RandIter>
   __advance_to(_RandIter& __iter,
-               __difference_type<_RandIter> __count,
+               __iter_difference_t<_RandIter> __count,
                const _RandIter& __sentinel,
                random_access_iterator_tag) {
     auto __dist = _IterOps::distance(__iter, __sentinel);
@@ -216,9 +204,6 @@ struct _IterOps<_ClassicAlgPolicy> {
   }
 };
 
-template <class _AlgPolicy, class _Iter>
-using __policy_iter_diff_t _LIBCPP_NODEBUG = typename _IterOps<_AlgPolicy>::template __difference_type<_Iter>;
-
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/__algorithm/lexicographical_compare_three_way.h b/libcxx/include/__algorithm/lexicographical_compare_three_way.h
index 442223e79e4ec..a350232af17b2 100644
--- a/libcxx/include/__algorithm/lexicographical_compare_three_way.h
+++ b/libcxx/include/__algorithm/lexicographical_compare_three_way.h
@@ -37,13 +37,13 @@ template <class _InputIterator1, class _InputIterator2, class _Cmp>
 _LIBCPP_HIDE_FROM_ABI constexpr auto __lexicographical_compare_three_way_fast_path(
     _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2, _Cmp& __comp)
     -> decltype(__comp(*__first1, *__first2)) {
-  static_assert(signed_integral<__iterator_difference_type<_InputIterator1>>,
+  static_assert(signed_integral<__iter_difference_t<_InputIterator1>>,
                 "Using a non-integral difference_type is undefined behavior.");
-  static_assert(signed_integral<__iterator_difference_type<_InputIterator2>>,
+  static_assert(signed_integral<__iter_difference_t<_InputIterator2>>,
                 "Using a non-integral difference_type is undefined behavior.");
 
-  using _Len1   = __iterator_difference_type<_InputIterator1>;
-  using _Len2   = __iterator_difference_type<_InputIterator2>;
+  using _Len1   = __iter_difference_t<_InputIterator1>;
+  using _Len2   = __iter_difference_t<_InputIterator2>;
   using _Common = common_type_t<_Len1, _Len2>;
 
   _Len1 __len1      = __last1 - __first1;
diff --git a/libcxx/include/__algorithm/make_heap.h b/libcxx/include/__algorithm/make_heap.h
index f98a0d2f89c85..0f98d6fd80ae6 100644
--- a/libcxx/include/__algorithm/make_heap.h
+++ b/libcxx/include/__algorithm/make_heap.h
@@ -33,10 +33,10 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
 __make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
   __comp_ref_type<_Compare> __comp_ref = __comp;
 
-  using __diff_t     = __iterator_difference_type<_RandomAccessIterator>;
+  using __diff_t     = __iter_difference_t<_RandomAccessIterator>;
   const __diff_t __n = __last - __first;
 
-  const bool __assume_both_children = is_arithmetic<__iterator_value_type<_RandomAccessIterator> >::value;
+  const bool __assume_both_children = is_arithmetic<__iter_value_t<_RandomAccessIterator> >::value;
 
   // While it would be correct to always assume we have both children, in practice we observed this to be a performance
   // improvement only for arithmetic types.
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index 749c701974f07..fd1e989467d2e 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -60,7 +60,7 @@ __mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Pred& __pred, _Pro
 template <class _Iter>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_Iter, _Iter>
 __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
-  using __value_type              = __iterator_value_type<_Iter>;
+  using __value_type              = __iter_value_t<_Iter>;
   constexpr size_t __unroll_count = 4;
   constexpr size_t __vec_size     = __native_vector_size<__value_type>;
   using __vec                     = __simd_vector<__value_type, __vec_size>;
diff --git a/libcxx/include/__algorithm/move.h b/libcxx/include/__algorithm/move.h
index 52bd5fb5253db..031560c3b1b76 100644
--- a/libcxx/include/__algorithm/move.h
+++ b/libcxx/include/__algorithm/move.h
@@ -80,8 +80,7 @@ struct __move_impl {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
   operator()(_InIter __first, _InIter __last, _OutIter __result) const {
     using _Traits = __segmented_iterator_traits<_OutIter>;
-    using _DiffT =
-        typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
+    using _DiffT  = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;
 
     if (__first == __last)
       return std::make_pair(std::move(__first), std::move(__result));
diff --git a/libcxx/include/__algorithm/move_backward.h b/libcxx/include/__algorithm/move_backward.h
index a4698327b474d..a6a7130b47719 100644
--- a/libcxx/include/__algorithm/move_backward.h
+++ b/libcxx/include/__algorithm/move_backward.h
@@ -86,8 +86,7 @@ struct __move_backward_impl {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
   operator()(_InIter __first, _InIter __last, _OutIter __result) const {
     using _Traits = __segmented_iterator_traits<_OutIter>;
-    using _DiffT =
-        typename common_type<__iterator_difference_type<_InIter>, __iterator_difference_type<_OutIter> >::type;
+    using _DiffT  = typename common_type<__iter_difference_t<_InIter>, __iter_difference_t<_OutIter> >::type;
 
     // When the range contains no elements, __result might not be a valid iterator
     if (__first == __last)
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index eea07e2b96b64..e0f4e9dac62ea 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -115,7 +115,7 @@ template <class _ExecutionPolicy,
           class _Predicate,
           class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
           enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI __iterator_difference_type<_ForwardIterator>
+_LIBCPP_HIDE_FROM_ABI __iter_difference_t<_ForwardIterator>
 count_if(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) {
   _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(
       _ForwardIterator, "count_if(first, last, pred) requires [first, last) to be ForwardIterators");
@@ -129,7 +129,7 @@ template <class _ExecutionPolicy,
           class _Tp,
           class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
           enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI __iterator_difference_type<_ForwardIterator>
+_LIBCPP_HIDE_FROM_ABI __iter_difference_t<_ForwardIterator>
 count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) {
   _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(
       _ForwardIterator, "count(first, last, val) requires [first, last) to be ForwardIterators");
diff --git a/libcxx/include/__algorithm/radix_sort.h b/libcxx/include/__algorithm/radix_sort.h
index 5549a69f5e220..290a36ca3cca0 100644
--- a/libcxx/include/__algorithm/radix_sort.h
+++ b/libcxx/include/__algorithm/radix_sort.h
@@ -72,14 +72,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #if _LIBCPP_STD_VER >= 14
 
 template <class _InputIterator, class _OutputIterator>
-_LIBCPP_HIDE_FROM_ABI constexpr pair<_OutputIterator, __iterator_value_type<_InputIterator>>
+_LIBCPP_HIDE_FROM_ABI constexpr pair<_OutputIterator, __iter_value_t<_InputIterator>>
 __partial_sum_max(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
   if (__first == __last)
     return {__result, 0};
 
-  auto __max                                  = *__first;
-  __iterator_value_type<_InputIterator> __sum = *__first;
-  *__result                                   = __sum;
+  auto __max                           = *__first;
+  __iter_value_t<_InputIterator> __sum = *__first;
+  *__result                            = __sum;
 
   while (++__first != __last) {
     if (__max < *__first) {
@@ -124,7 +124,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __nth_radix(size_t __radix_number, _Radix _
 template <class _ForwardIterator, class _Map, class _RandomAccessIterator>
 _LIBCPP_HIDE_FROM_ABI constexpr void
 __collect(_ForwardIterator __first, _ForwardIterator __last, _Map __map, _RandomAccessIterator __counters) {
-  using __value_type = __iterator_value_type<_ForwardIterator>;
+  using __value_type = __iter_value_t<_ForwardIterator>;
   using __traits     = __counting_sort_traits<__value_type, _Map>;
 
   std::for_each(__first, __last, [&__counters, &__map](const auto& __preimage) { ++__counters[__map(__preimage)]; });
@@ -160,7 +160,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __collect_impl(
     _RandomAccessIterator1 __counters,
     _RandomAccessIterator2 __maximums,
     index_sequence<_Radices...>) {
-  using __value_type                 = __iterator_value_type<_ForwardIterator>;
+  using __value_type                 = __iter_value_t<_ForwardIterator>;
   constexpr auto __radix_value_range = __radix_sort_traits<__value_type, _Map, _Radix>::__radix_value_range;...
[truncated]

#if _LIBCPP_STD_VER >= 23
template <class _InputIterator>
using __iter_key_type _LIBCPP_NODEBUG = remove_const_t<tuple_element_t<0, __iterator_value_type<_InputIterator>>>;
using __iter_key_type _LIBCPP_NODEBUG = remove_const_t<tuple_element_t<0, __iter_value_t<_InputIterator>>>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were using this in the deduction guides for e.g. flat_map:

-> flat_map<__iter_key_type<_InputIterator>, __iter_mapped_type<_InputIterator>, _Compare>;

Were we conforming prior to this change since we were always using iterator_traits? It would be surprising.

I imagine that either there's a LWG issue because the spec is using iterator_traits for flat_map when it should use something more recent, or we used to have a slight conformance bug. If the latter, let's add test coverage for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were conforming. The constraints are introduced by the exact same wording across the map types AFAICT. It also seems intentional to me, since the rest of the wording is rather close to the legacy containers.

template <>
struct _IterOps<_ClassicAlgPolicy> {
template <class _Iter>
using __value_type _LIBCPP_NODEBUG = typename iterator_traits<_Iter>::value_type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it okay to make this (general) change? I understand that we are probably just accepting more code than previously, but it is possible for conforming C++20 code to break when we do this?

A few questions:

  • In C++20, is it possible for an iterator to have different types for std::iter_value_t<It> and std::iterator_traits<It>::value_type?
  • If so, is it possible for these two types to differ to the extent that a classic algorithm (which should in principle use iterator_traits, or at least did historically) would break if we start using std::iter_value_t<It> instead?
  • If it's possible for these two typedefs to differ and to the extent of code breaking, are these cases so far fetched that we don't care to support them?
  • The same set of questions holds for every nested typedef we're modifying here.

Also CC @frederick-vs-ja since I think I remember having that discussion in a previous patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your proofs:

  1. For iter_reference_t, https://eel.is/c++draft/iterator.cpp17#input.iterators-2 says that *a must be iterator_traits::reference for a C++17 iterator. Hence iterator_traits::reference and decltype(*a) == iter_reference_t<It> must be the same. ✅

  2. For iter_difference_t, we have two cases. If iterator_traits<It> is specialized, incrementable.traits says that iter_difference_t<It> is iterator_traits<It>::difference_type. If not specialized, then iter_difference_t<It> is incrementable_traits<It>::difference_type, whatever that is. However, in C++20, iterator_traits also changed to be more C++20 friendly (I guess):

    • if It::difference_type and It::value_type and It::reference (etc..) do exist, then iterator_traits<It>::difference_type is It::difference_type. However, in that case, incrementable_traits<It>::difference_type is also It::difference_type, unless it is specialized explicitly. And it's legal to specialize it explicitly. This means that technically, if someone specializes incrementable_traits<It> to give it a difference_type that doesn't match iterator_traits<It>::difference_type, this patch will produce a difference. It's unclear whether

      Users may specialize incrementable_traits on program-defined types.

      is intended to override the normal requirement that specializations must be equivalent to the base template. This is probably material for a quick LWG issue. ❌

    • if It::difference_type or any of its friends do not exist, iterator_traits<It>::difference_type is defined to be incrementable_traits<It>::difference_type, so the equivalence holds. ✅

  3. For iter_value_t, iter_value_t<It> is iterator_traits<It>::value_type if iterator_traits<It> is specialized. Otherwise, it is indirectly_readable_traits<It>::value_type, whatever that is. However,

    • if It::difference_type and It::value_type and It::reference (etc..) do exist, then iterator_traits<It>::value_type is I::value_type. And in that case indirectly_readable_traits<It>::value_type is a rather complicated thing. It seems that if It::element_type and It::value_type disagree, indirectly_readable_traits<It>::value_type would not be defined whereas iterator_traits<It>::value_type would be. There's also the specialization problem above. And there's the issue of legacy output iterators where It::value_type == void, as explained in this note. ❌
    • if It::value_type or any of its friends do not exist, iterator_traits<It>::value_type is defined to indirectly_readable_traits<It>::value_type. ✅

So, in summary, I think we have the following issues:

  • iterator_traits<It>::difference_type may be It::difference_type but incrementable_traits<It> may be something else via a specialization
  • iterator_traits<It>::value_type may be It::value_type but indirectly_readable_traits<It>::value_type does not exist
  • iterator_traits<It>::value_type may be It::value_type but indirectly_readable_traits<It> may be specialized
  • iterator_traits<It>::value_type may be It::value_type == void but indirectly_readable_traits<It> does not exist

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, the question is whether we actually care about these differences.

I think we don't care about the specialization stuff. We should get those clarified with a LWG issue, but I have no sympathy for someone who has different It::difference_type and incrementable_traits<It>::difference_type through a specialization. Same for It::value_type.

However, the case of legacy output iterators could be a real problem. However, we basically never use iterator_traits<It>::value_type for an output iterator since it may be void. So in practice this might not matter.

I'll wait for @frederick-vs-ja to chime in, but so far I think I'd be comfortable with making this change. However, this would have to be dry-run on large code bases (we can ping the Google folks and we'll also do an internal build when we're ready), to shake off any unintended consequences.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I submitted LWG4080. Perhaps we should add some discussions to it.

  • It's unclear whether

    Users may specialize incrementable_traits on program-defined types.

    is intended to override the normal requirement that specializations must be equivalent to the base template.

I think overriding was intended. The standard library contains such design for common_iterator, although this looks quite questionable to me. If overriding is considered harmful (as it can create inconsistent value types), I think it makes more sense to disallow some user-provided specializations (and slightly fix common_iterator).

  • iterator_traits<It>::value_type may be It::value_type but indirectly_readable_traits<It>::value_type does not exist

I'm more worried about this. There can be C++17 iterators with evil element_type (I initially reported this in microsoft/STL#4436) which make them not C++20 iterators. If we use __iter_value_t like MSVC STL's current approach, some code using legacy algorithms can be accepted in C++17 but rejected in C++20.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really not that worried about a value_type/element_type mismatch. That rather seems to me like something worth diagnosing, since it's almost certainly user error. I'm not sure it's actually specified anywhere, but the intention that remove_reference_t<decltype(*iter)> is element_type seems rather clear to me.

@philnik777 philnik777 force-pushed the merge_iter_traits_aliases branch from 44b6383 to 435b204 Compare October 16, 2025 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants